#include "func.h"
#include <string.h>
#include <iostream>
#include <shadow.h>
#include <crypt.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
using std::string;
using std::cin;
using std::cout;
using std::endl;
using namespace std;
#define TASK_TYPE_LOGIN_SECTION1 1
#define TASK_TYPE_LOGIN_SECTION1_RESP_OK 2
#define TASK_TYPE_LOGIN_SECTION1_RESP_ERROR 3
#define TASK_TYPE_LOGIN_SECTION2 4
#define TASK_TYPE_LOGIN_SECTION2_RESP_OK 5
#define TASK_TYPE_LOGIN_SECTION2_RESP_ERROR 6
#define TASK_TYPE_REGISTER_SECTION1      7   // 注册阶段1：发送用户名
#define TASK_TYPE_REGISTER_SECTION1_RESP_OK 8// 注册阶段1成功：返回盐值
#define TASK_TYPE_REGISTER_SECTION1_RESP_ERROR 9// 注册阶段1失败：如用户名已存在
#define TASK_TYPE_REGISTER_SECTION2      10  // 注册阶段2：发送加密密码
#define TASK_TYPE_REGISTER_SECTION2_RESP_OK 11// 注册阶段2成功：注册完成
#define TASK_TYPE_REGISTER_SECTION2_RESP_ERROR 12// 注册阶段2失败：如数据库错误
struct Packet
{
    int _type;
    int _length;
    string _msg;
};

//md5格式
string md5Crypt(const string &password,const string &salt)
{
    //符合$1$salt$格式
    string setting=salt;
    //使用crypt函数生成MD5密文
    char *encrypted=crypt(password.c_str(),setting.c_str());
    if(!encrypted)
    {
        cerr<<"crypt failed"<<endl;
        return "";
    }
    return string(encrypted);
}

//生成随机盐值
string generateSalt()
{
    const string chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    string salt;
    srand(time(nullptr));
    for(int i=0;i<8;i++)
    {
        salt+=chars[rand()%chars.size()];
    }
    return salt;
}
//发送TLV数据包
bool sendPacket(int fd,int type,const string &data)
{
    int net_type=htonl(type);
    int net_length=htonl(data.length());

    //发送packet
    int ret=send(fd,&net_type,sizeof(net_type),0);
    if(ret !=sizeof(net_type))
    {
        perror("发送类型失败");
        return false;
    }
    ret=send(fd,&net_length,sizeof(net_length),0);
    if(ret !=sizeof(net_length))
    {
        perror("发送长度失败");
        return false;
    }
    //发送数据内容
    if(data.length()>0)
    {
        ret=send(fd,data.c_str(),data.length(),0);
        if(ret!=(int)data.length())
        {
            perror("发送数据失败");
            return false;
        }
    }
    cout << "Sent Packet: 类型=" << type 
        << ", 长度=" << data.length() 
        << ", 内容=" << data << endl;

    return true;
}

//接收整个数据包
bool recvPacket(int fd,Packet &packet)
{

    char header[8];
    cout << "DEBUG: 开始接收数据包头部（8字节）..." << endl;
    int ret=recv(fd,header,sizeof(header),MSG_WAITALL);
    if(ret != sizeof(header)) {
        if(ret == 0) {
            cout << "Server closed connection" << endl;
        } else {
            perror("recv type failed");
        }
        return false;
    }
    cout << "DEBUG: 成功接收头部（8字节）" << endl;

    //解析类型和长度网络字节序
    int net_type,net_length;
    memcpy(&net_type,header,sizeof(net_type));
    memcpy(&net_length,header+sizeof(net_type),sizeof(net_length));

    //转换字节序    
    packet._type = ntohl(net_type);
    packet._length = ntohl(net_length);  // 转换为主机字节序
    cout << "DEBUG: 解析头部 - 类型: " << packet._type 
        << ", 数据长度: " << packet._length << endl;

    //接收data
    if(packet._length>0)
    {
        cout << "DEBUG: 开始接收数据内容（" << packet._length << "字节）..." << endl;
        char *buf=new char[packet._length+1]();
        ret=recv(fd,buf,packet._length,MSG_WAITALL);
        if(ret!=packet._length)
        {
            delete[]buf;
            return false;
        }
        buf[packet._length]='\0';
        packet._msg=string(buf,packet._length);
        delete[]buf;
        cout << "DEBUG: 成功接收数据内容" << endl;
    }
    else
    {
        packet._msg="";
    }

    cout << "Received TLV: 类型=" << packet._type 
        << ", 数据长度=" << packet._length
        << ", 数据内容=" << packet._msg << endl;
    return true;

}
//注册流程
bool registerProcess(int clientfd)
{
    string username;
    cout<<"请输入用户名";
    cin>>username;
    cin.ignore();//清除输入缓冲区的换行符

    if (!sendPacket(clientfd,TASK_TYPE_REGISTER_SECTION1,username))
    {
       cerr<<"注册阶段1：发送用户名失败"<<endl;
       return false;
    }

    //接收阶段1响应
    Packet resp;
    if(!recvPacket(clientfd,resp))
    {
        cerr<<"注册阶段1，接收响应失败"<<endl;
        return false;
    }
    //类型3表示错误
    if(resp._type==TASK_TYPE_REGISTER_SECTION1_RESP_ERROR)
    {
        cout<<"注册失败"<<resp._msg<<endl;
        return -1;
    }
    //类型2表示成功，返回盐值
    if(resp._type!=TASK_TYPE_REGISTER_SECTION1_RESP_OK)
    {
        cout<<"注册阶段1：协议错误"<<endl;
        return -1;
    }
    
    //阶段2接收盐值并发送加密密码
    string salt=resp._msg;
    string password;
    cout<<"请输入注册的密码";
    cin>>password;
    cin.ignore();
    
    //加密密码
    string encryptedPwd=md5Crypt(password,salt);
    if(encryptedPwd.empty())
    {
        cerr<<"注册阶段2：生成加密密码失败"<<endl;
        return false;
    }

    //发送加密密码
    if(!sendPacket(clientfd,TASK_TYPE_REGISTER_SECTION2,username+":"+encryptedPwd))
    {
        cerr<<"发送加密密码失败"<<endl;
        return false;
    }

    //接收阶段2响应
    if(!recvPacket(clientfd,resp))
    {
        cerr<<"注册阶段2，接收响应失败"<<endl;
        return false;
    }
    //阶段2类型2表示成功
    if(resp._type==TASK_TYPE_REGISTER_SECTION2_RESP_OK)
    {
        cout<<"加密密码注册成功"<<endl;
        return true;
    }

    //类型3表示错误
    else if(resp._type==TASK_TYPE_REGISTER_SECTION2_RESP_ERROR)
    {
        cout<<"加密密码注册失败"<<resp._msg<<endl;
        return false;
    }
    else
    {
        cout<<"加密密码协议错误"<<endl;
        return -1;
    }
}

//登陆阶段
bool loginProcess(int clientfd)
{
   //阶段1发送用户名获取盐值
   string username;
   cout<<"请输入登录用户名";
   cin>>username;
   cin.ignore();
   if (!sendPacket(clientfd,TASK_TYPE_LOGIN_SECTION1,username))
    {
       cerr<<"注册阶段1：发送用户名失败"<<endl;
       return false;
    }
    //接收阶段1响应
    Packet resp;
    if(!recvPacket(clientfd,resp))
    {
        cerr<<"注册阶段1，接收响应失败"<<endl;
        return false;
    }
    //类型3表示错误
    if(resp._type==TASK_TYPE_LOGIN_SECTION1_RESP_ERROR)
    {
        cout<<"登录失败"<<resp._msg<<endl;
        return -1;
    }
    //类型2表示协议错误，返回盐值
    if(resp._type!=TASK_TYPE_LOGIN_SECTION1_RESP_OK)
    {
        cout<<"登录阶段：协议错误"<<endl;
        return -1;
    }
    //阶段2接收盐值并发送加密密码
    string salt=resp._msg;
    string password;
    cout<<"请输入注册的密码";
    cin>>password;
    cin.ignore();
    
    //加密密码
    string encryptedPwd=md5Crypt(password,salt);
    if(encryptedPwd.empty())
    {
        cerr<<"注册阶段2：加密密码失败"<<endl;
        return false;
    }

    //发送加密密码
    string sendData=username +":"+ encryptedPwd;
    if(!sendPacket(clientfd,TASK_TYPE_LOGIN_SECTION2,sendData))
    {
        cerr<<"发送加密密码失败"<<endl;
        return false;
    }

    //接收阶段2响应
    if(!recvPacket(clientfd,resp))
    {
        cerr<<"注册阶段2，接收响应失败"<<endl;
        return false;
    }
    //阶段2类型2表示成功
    if(resp._type==TASK_TYPE_LOGIN_SECTION2_RESP_OK)
    {
        cout<<"登录成功"<<endl;
        return true;
    }

    //类型3表示错误
    else if(resp._type==TASK_TYPE_LOGIN_SECTION2_RESP_ERROR)
    {
        cout<<"登录失败"<<resp._msg<<endl;
        return false;
    }
    else
    {
        cout<<"登录协议错误"<<endl;
        return -1;
    }


}

int main()
{

    //创建客户端套接字
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    ERROR_CHECK(cfd, -1, "socket");

    //连接服务器
    struct sockaddr_in server_addr;  // 改名为server_addr更合适
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8000);

    int ret = connect(cfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
    ERROR_CHECK(ret, -1, "connect");

    cout << "Connection established successfully!" << endl;
    while(1)
    {
        string input;
        cout<<">> input";
        getline(cin,input);

        if(input=="exit")
        {
            break;
        }
        else if(input=="register")
        {
           registerProcess(cfd);
           continue;
        }
        else if(input=="login")
        {
            loginProcess(cfd);
            continue;
        }


        //发送数据给服务器
        if(!sendPacket(cfd,1,input))
        {
            close(cfd);
            return -1;
        }

        //接收
        Packet response;
        if(!recvPacket(cfd,response))
        {
            close(cfd);
            return -1;
        }

        cout<<"服务器回复"<<response._msg<<endl;
    }
    close(cfd);
    return 0;
}



